package me.sdevil507.org.service.impl;

import com.google.common.collect.Lists;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.extern.slf4j.Slf4j;
import me.sdevil507.biz.common.exceptions.KeywordExistedException;
import me.sdevil507.biz.common.exceptions.OrgNameEnExistedException;
import me.sdevil507.biz.common.exceptions.OrgNameZhExistedException;
import me.sdevil507.biz.common.sysdict.po.QSysDict;
import me.sdevil507.org.dto.OrgKeywordParamsDto;
import me.sdevil507.org.dto.OrgParamsDto;
import me.sdevil507.org.mapper.OrgKeywordMapper;
import me.sdevil507.org.mapper.OrgMapper;
import me.sdevil507.org.po.*;
import me.sdevil507.org.repository.*;
import me.sdevil507.org.service.OrgRoleService;
import me.sdevil507.org.service.OrgService;
import me.sdevil507.platform.repository.PlatResourceRepository;
import me.sdevil507.services.dto.OrgClassifyResultDTO;
import me.sdevil507.services.dto.OrgResultDTO;
import me.sdevil507.services.dto.OrgTypeResultDTO;
import me.sdevil507.supports.jpa.PageParamsDTO;
import me.sdevil507.supports.jpa.SortParamsDTO;
import me.sdevil507.supports.shiro.helper.OrgAccountHelper;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 机构[OrgServiceImpl]
 *
 * @author sdevil507
 * created on 2020-07-13 13:40:20
 */
@Service
@Slf4j
public class OrgServiceImpl implements OrgService {

    @Autowired
    JPAQueryFactory jpaQueryFactory;

    @Autowired
    OrgRoleService orgRoleService;

    @Resource
    private OrgRepository orgRepository;

    @Resource
    private OrgUserRelRepository orgUserRelRepository;

    @Autowired
    private OrgMapper orgMapper;

    @Autowired
    private OrgKeywordMapper orgKeywordMapper;

    @Autowired
    private OrgKeywordRepository orgKeywordRepository;

    @Autowired
    private OrgDeptRepository orgDeptRepository;

    @Autowired
    private OrgRoleRepository orgRoleRepository;

    @Autowired
    private OrgResourceRepository orgResourceRepository;

    @Autowired
    private PlatResourceRepository platResourceRepository;

    @Override
    public OrgPo readOne(Long orgId) {
        return orgRepository.findById(orgId).orElse(null);
    }

    @Override
    public List<OrgUserRelPo> readOrgsByAccId() {
        QOrgUserRelPo qOrgUserRelPo = QOrgUserRelPo.orgUserRelPo;
        Predicate predicate = qOrgUserRelPo.user.id.eq(OrgAccountHelper.getUserId()).and(qOrgUserRelPo.mgr.isTrue());
        return Lists.newArrayList(orgUserRelRepository.findAll(predicate));
    }

    @Override
    public List<OrgPo> readAll() {
        QOrgPo qOrgPo = QOrgPo.orgPo;

        // 开启谓语查询条件
        Predicate predicate = qOrgPo.id.isNotNull().and(
                qOrgPo.deleted.isFalse()
        );

        // 排序
        Sort sort = Sort.by(new Sort.Order(Sort.Direction.ASC, "createdDate"));

        // 执行Iterable转换List
        return Lists.newArrayList(orgRepository.findAll(predicate, sort).iterator());
    }

    @Override
    public Page<OrgPo> readAllByPage(OrgParamsDto searchParamsDto, PageParamsDTO pageParamsDto, SortParamsDTO sortParamsDto) {
        QOrgPo qOrgPo = QOrgPo.orgPo;

        // 开启谓语查询条件
        Predicate predicate = qOrgPo.id.isNotNull().and(
                qOrgPo.deleted.isFalse()
        );

        String nameZh = searchParamsDto.getNameZh();
        if (!StringUtils.isEmpty(nameZh)) {
            predicate = ExpressionUtils.and(predicate, qOrgPo.nameZh.like("%" + nameZh + "%"));
        }

        String nameEn = searchParamsDto.getNameEn();
        if (!StringUtils.isEmpty(nameEn)) {
            predicate = ExpressionUtils.and(predicate, qOrgPo.nameEn.like("%" + nameEn + "%"));
        }

        // 排序
        Sort sort = sortParamsDto.getSort();
        if (null == sort) {
            sort = Sort.by(new Sort.Order(Sort.Direction.DESC, "createdDate"));
        }

        // 执行返回
        Pageable pageable = pageParamsDto.getPageableWithSort(sort);
        return orgRepository.findAll(predicate, pageable);
    }

    @Override
    public void create(OrgParamsDto dto) throws OrgNameZhExistedException, OrgNameEnExistedException {
        if (nameZhExisted(dto.getNameZh(), null)) {
            throw new OrgNameZhExistedException("机构中文名称重复,请确认该机构是否已经存在!");
        }

        if (nameEnExisted(dto.getNameEn(), null)) {
            throw new OrgNameEnExistedException("机构英文名称重复,请确认该机构是否已经存在!");
        }

        OrgPo orgPo = orgMapper.dto2po(dto);
        orgPo.setApprove(true);
        orgRepository.save(orgPo);
        // 创建机构默认部门
        OrgDeptPo orgDeptPo = new OrgDeptPo();
        orgDeptPo.setOrg(orgPo);
        orgDeptPo.setTitle(dto.getNameZh());
        orgDeptPo.setSortNum(0);
        orgDeptPo.setDeleted(false);
        orgDeptPo.setParentId(null);
        orgDeptRepository.save(orgDeptPo);
        // 统一预置超管角色与资源
//        List<PlatResourcePo> resources = platResourceRepository.findAll();
//        OrgRolePo orgRolePo = new OrgRolePo();
//        orgRolePo.setName("超级管理员");
//        orgRolePo.setDescription("智库机构端超级管理员");
//        orgRolePo.setSortNum(1);
//        orgRolePo.setCode("admin");
//        orgRolePo.setPreset(true);
//        Set<OrgPo> orgs = new HashSet<>();
//        orgs.add(orgPo);
//        orgRolePo.setOrgs(orgs);
//
//        orgRolePo.setResources(new HashSet<>(resources));
//        orgRoleRepository.save(orgRolePo);
    }

    @Override
    public void update(OrgParamsDto dto) throws OrgNameZhExistedException, OrgNameEnExistedException {
        OrgPo po = orgRepository.findById(dto.getId()).orElse(null);
        if (null != po) {
            if (nameZhExisted(dto.getNameZh(), dto.getId())) {
                throw new OrgNameZhExistedException("机构中文名称重复,请确认该机构是否已经存在!");
            }

            if (nameEnExisted(dto.getNameEn(), dto.getId())) {
                throw new OrgNameEnExistedException("机构英文名称重复,请确认该机构是否已经存在!");
            }

            orgMapper.updatePoFromDto(dto, po);
            orgRepository.save(po);
        }
    }

    @Override
    public void delete(OrgParamsDto dto) {
        orgRepository.logicDelete(dto.getId());
    }

    @Override
    public void enable(OrgParamsDto dto) {
        orgRepository.enable(dto.getId(), dto.getEnable());
    }

    @Override
    public List<OrgKeywordPo> readAllKeywords(Long orgId) {
        OrgPo orgPo = orgRepository.findById(orgId).orElse(null);
        if (orgPo != null) {
            return new ArrayList<>(orgPo.getKeywords());
        }
        return null;
    }

    @Override
    public void updateKeywords(OrgParamsDto dto) {
        OrgPo orgPo = orgRepository.findById(dto.getId()).orElse(null);
        if (orgPo != null) {
            orgKeywordRepository.deleteAll(orgPo.getKeywords());
            for (OrgKeywordParamsDto keyword : dto.getKeywords()) {
                OrgKeywordPo tempOrgKeywordPo = orgKeywordMapper.dto2po(keyword);
                tempOrgKeywordPo.setOrg(orgPo);
                orgKeywordRepository.save(tempOrgKeywordPo);
            }
        }
    }

    @Override
    public void createKeyword(OrgKeywordParamsDto dto) throws KeywordExistedException {
        if (orgKeywordRepository.existsByName(dto.getName())) {
            throw new KeywordExistedException("关键词在系统中已经存在!");
        }
        OrgKeywordPo po = new OrgKeywordPo();
        po.setName(dto.getName());
        po.setOrg(orgRepository.findById(dto.getOrgId()).orElse(null));
        orgKeywordRepository.save(po);
    }

    @Override
    public void deleteKeyword(String name) {
        orgKeywordRepository.deleteByName(name);
    }

    /**
     * 判断机构中文名称是否存在
     *
     * @param name 名称
     * @param id   id
     * @return 执行反馈
     */
    private boolean nameZhExisted(String name, Long id) {
        QOrgPo qOrgPo = QOrgPo.orgPo;
        Predicate predicate = qOrgPo.id.isNotNull()
                .and(qOrgPo.deleted.eq(false))
                .and(qOrgPo.nameZh.eq(name));
        if (null != id) {
            predicate = ExpressionUtils.and(predicate, qOrgPo.id.ne(id));
        }
        return orgRepository.exists(predicate);
    }

    /**
     * 判断机构英文名称是否存在
     *
     * @param name 名称
     * @param id   id
     * @return 执行反馈
     */
    private boolean nameEnExisted(String name, Long id) {
        QOrgPo qOrgPo = QOrgPo.orgPo;
        Predicate predicate = qOrgPo.id.isNotNull()
                .and(qOrgPo.deleted.eq(false))
                .and(qOrgPo.nameEn.eq(name));
        if (null != id) {
            predicate = ExpressionUtils.and(predicate, qOrgPo.id.ne(id));
        }
        return orgRepository.exists(predicate);
    }

    @Cacheable(cacheNames = "org_test", key = "'typeKey_' + #p0")
    @Override
    public String test(Long userId) {
        log.info("test，获取字典表数据,Time：" + new Date());
        return "111";
    }

    /**
     * 获取所有智库的类型并分组
     *
     * @return
     */
    @Cacheable(cacheNames = "orgTypeList", key = "'userId_' + #p0")
    @Override
    public Map getAllOrgType(Long userId) {
        log.info("orgTypeList无缓存时执行下面代码，获取字典表数据,Time：" + new Date());

        //权限下机构数据
        List<OrgResultDTO> orgResultDTOList = new ArrayList<>();

        QOrgPo qOrgPo = QOrgPo.orgPo;
        QSysDict qSysDict = QSysDict.sysDict;
        LinkedHashMap<String, List<Long>> map = new LinkedHashMap<>();
        List<OrgPo> orgPoList = orgRepository.findAllById(OrgAccountHelper.getOrgIdList());
        //对所有的类别进行分组-去重
        Set<String> classifyList = new HashSet<>();
        orgPoList.stream().forEach(
                orgPo -> {
                    String cs = orgPo.getClassify();
                    if(cs.contains(",")){
                        classifyList.addAll(Arrays.asList(cs.split(",")));
                        String[] classifyes = cs.split(",");
                        for (String cf : classifyes) {
                            //循环存储机构数据
                            OrgResultDTO orgDto = new OrgResultDTO();
                            orgDto.setId(orgPo.getId());
                            orgDto.setName(orgPo.getNameZh());
                            orgDto.setShortName(orgPo.getShortNameZh());
                            orgDto.setClassify(cf);
                            orgResultDTOList.add(orgDto);
                        }
                    }else{
                        classifyList.add(orgPo.getClassify());
                        //存储机构数据
                        OrgResultDTO orgDto = new OrgResultDTO();
                        orgDto.setId(orgPo.getId());
                        orgDto.setName(orgPo.getNameZh());
                        orgDto.setShortName(orgPo.getShortNameZh());
                        orgDto.setClassify(orgPo.getClassify());
                        orgResultDTOList.add(orgDto);
                    }
                }
        );

        Map<Integer, String> sysdictResultMap = new HashMap<>();
        //查询对应的字典表的值
        jpaQueryFactory.select(qSysDict.dname, qSysDict.dvalue)
                .from(qSysDict)
                .where(qSysDict.dvalue.in(classifyList).and(qSysDict.typeKey.eq("ThinkTankClassify")))
                .fetch().stream()
                .forEach(tuple -> {
                    sysdictResultMap.put(Integer.parseInt(tuple.get(qSysDict.dvalue)), tuple.get(qSysDict.dname));
                });
        //按键值排序
        Map<Integer, String> sortedMap = sysdictResultMap.entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(
                        Collectors.toMap(
                                Map.Entry::getKey,
                                Map.Entry::getValue,
                                (oldVal, newVal) -> oldVal,
                                LinkedHashMap::new
                        )
                );
        //比对
        sortedMap.entrySet().forEach(entry->{
            //存储机构类型数据
            List<Long> orgList = map.get(entry.getValue());
            if (CollectionUtils.isEmpty(orgList)) {
                orgList = new ArrayList<Long>();
            }
            //判断机构的分类是否包含去重的字典表的值
            for (OrgPo orgPo : orgPoList) {
                //机构的类别
                String orgCs = orgPo.getClassify();
                //如果机构的类别在map里面
                if(orgCs.contains(entry.getKey()+"")){
                    orgList.add(orgPo.getId());
                }
            }
            map.put(entry.getValue(), orgList);
        });;


//        //查询所有的机构类型
//        jpaQueryFactory.select(
//                Projections.constructor(OrgTypeTempDTO.class,
//                        qOrgPo.id,
//                        qOrgPo.nameZh.as("name"),
//                        qSysDict.dname.as("orgTypeName"),
//                        qSysDict.dvalue.as("orgTypeValue")
//                )
//        )
//        .from(qOrgPo)
//        .leftJoin(qSysDict).on(qOrgPo.classify.contains(qSysDict.dvalue).and(qSysDict.typeKey.eq("ThinkTankClassify")))
//        .where(qOrgPo.deleted.isFalse().and(qOrgPo.id.in(orgIds)))
//        .fetch()
//        .stream()
//        .forEach(dto -> {
//            //存储机构类型数据
//            List<Long> orgList = map.get(dto.getOrgTypeName());
//            if (CollectionUtils.isEmpty(orgList)) {
//                orgList = new ArrayList<Long>();
//            }
//            orgList.add(dto.getId());
//            map.put(dto.getOrgTypeName(), orgList);
//            //存储机构数据
//            OrgResultDTO orgDto = new OrgResultDTO();
//            orgDto.setId(dto.getId());
//            orgDto.setName(dto.getName());
//            orgResultDTOList.add(orgDto);
//        });
        //map转成list
        List<OrgTypeResultDTO> orgTypeList = map.entrySet().stream().map(et ->{
            OrgTypeResultDTO searchResultDTO = new OrgTypeResultDTO();
            searchResultDTO.setName(et.getKey());
            searchResultDTO.setOrgIdList(et.getValue());
            return searchResultDTO;
        }).collect(Collectors.toList());

        //所有智库分类详细
        Map<String, List<OrgResultDTO>> orgClassifyMap = orgResultDTOList.stream().collect(Collectors.groupingBy(OrgResultDTO::getClassify));
        List<OrgClassifyResultDTO> orgClassifyList = new ArrayList<>();
        for (Map.Entry<String, List<OrgResultDTO>> entry : orgClassifyMap.entrySet()) {
            OrgClassifyResultDTO ocr = new OrgClassifyResultDTO();
            ocr.setClassify(Integer.parseInt(entry.getKey()));
            ocr.setName(sortedMap.get(Integer.parseInt(entry.getKey())));
            ocr.setOrgClassifyList(entry.getValue());
            orgClassifyList.add(ocr);
        }

        Map resultMap = new HashMap();
        resultMap.put("orgTypeList", orgTypeList);
        resultMap.put("orgList", orgResultDTOList);
        orgClassifyList = orgClassifyList.stream().sorted(Comparator.comparing(OrgClassifyResultDTO::getClassify)).collect(Collectors.toList());
        resultMap.put("orgClassifyList", orgClassifyList);

        return resultMap;
    }


    @CacheEvict(value = "orgTypeList", key = "'userId_' + #p0")
    @Override
    public void deleteAllOrgTypeOfUserId(Long userId) {
    }

    @Override
    public OrgPo getOrgInfoByPreDomainName(String preDomainName) {
        return orgRepository.findOrgPoByPreDomainName(preDomainName);
    }

    @Override
    public OrgPo getOrgInfoByOrgName(String orgName) {
        return orgRepository.findOrgPoByNameZhAndDeleted(orgName, false);
    }

    @Override
    public OrgPo getOrgInfoByOrgId(long orgId) {
        return orgRepository.getOne(orgId);
    }

    @Override
    public String getClassifyName(String[] classify) {
        return orgRepository.getClassifyName(classify);
    }

    @Override
    public Long getOrgIdByUserId(Long userId) {
        QOrgUserRelPo qOrgUserRelPo = QOrgUserRelPo.orgUserRelPo;
        return jpaQueryFactory.select(qOrgUserRelPo.org.id).from(qOrgUserRelPo).where(qOrgUserRelPo.user.id.eq(userId).and(qOrgUserRelPo.mgr.isTrue())).fetchOne();
    }
}